--- title: Transformations for 3D medical images keywords: fastai sidebar: home_sidebar nb_path: "nbs/03_transforms.ipynb" ---
{% raw %}
{% endraw %} {% raw %}
{% endraw %}

Resizing

{% raw %}

TensorDicom3D.resize_3d[source]

TensorDicom3D.resize_3d(t:TensorMask3D'>), size, scale_factor=None, mode='trilinear', align_corners=True, recompute_scale_factor=None)

A function to resize a 3D image using torch.nn.functional.interpolate

Args: t (Tensor): a 3D or 4D Tensor to be resized size (int): a tuple with the new x,y,z dimensions of the tensor after resize scale_factor, mode, align_corners, recompute_scale_factor: args from F.interpolate Returns: A new Tensor with Tensor.size = size

{% endraw %} {% raw %}

TensorMask3D.resize_3d[source]

TensorMask3D.resize_3d(t:TensorMask3D'>), size, scale_factor=None, mode='trilinear', align_corners=True, recompute_scale_factor=None)

A function to resize a 3D image using torch.nn.functional.interpolate

Args: t (Tensor): a 3D or 4D Tensor to be resized size (int): a tuple with the new x,y,z dimensions of the tensor after resize scale_factor, mode, align_corners, recompute_scale_factor: args from F.interpolate Returns: A new Tensor with Tensor.size = size

{% endraw %} {% raw %}

index_based_resize[source]

index_based_resize(t:TensorMask3D, size:int)

resizes a Tensor without creating new values

{% endraw %} {% raw %}

class Resize3D[source]

Resize3D(size, scale_factor=None, mode='trilinear', align_corners=True, recompute_scale_factor=None, **kwargs) :: RandTransform

A transform that before_call its state at each __call__

{% endraw %} {% raw %}
{% endraw %} {% raw %}
original = TensorDicom3D.create('/media/ScaleOut/prostata/data/dcm/A0042197734/T2/DICOM')
mask = TensorMask3D.create('/media/ScaleOut/prostata/data/dcm/A0042197734/T2/Annotation/Annotation.nii.gz')
{% endraw %} {% raw %}
original.show()
{% endraw %} {% raw %}
mask.show()
{% endraw %} {% raw %}
Resize3D((10,50,50))(original, split_idx = 0).show()
Resize3D((10,50,50))(mask, split_idx = 0).show()
{% endraw %}

Padding

Change size of tensor without changing size of the raw pixel data

{% raw %}

TensorDicom3D.pad_to[source]

TensorDicom3D.pad_to(im:TensorMask3D'>), new_size:tuple)

{% endraw %} {% raw %}

TensorMask3D.pad_to[source]

TensorMask3D.pad_to(im:TensorMask3D'>), new_size:tuple)

{% endraw %} {% raw %}

class Pad3D[source]

Pad3D(new_size, p=1, **kwargs) :: RandTransform

A transform that before_call its state at each __call__

{% endraw %} {% raw %}
{% endraw %} {% raw %}
Pad3D((10, 800, 800))(original).show()
{% endraw %}

Flipping

In medical images, the left and right side often cannot be differentiated from each other (e.g. scans of the head, hand, knee, ...). Therfore the image orientation is stored in the image header, enabeling the viewer system to accuratly display the images. For deep learning, only the pixel array is extracted, so the header information is lost. When displaying only the pixel array, the images might already be displayed flipped or in inverted slice order. So, implementing vertical/horizontal flipping as well as flipping alongside the z axis can be used for data augmentation.

{% raw %}

TensorDicom3D.flip_ll_3d[source]

TensorDicom3D.flip_ll_3d(t:TensorMask3D'>))

flips an image laterolateral

{% endraw %} {% raw %}

TensorMask3D.flip_ll_3d[source]

TensorMask3D.flip_ll_3d(t:TensorMask3D'>))

flips an image laterolateral

{% endraw %} {% raw %}

TensorDicom3D.flip_ap_3d[source]

TensorDicom3D.flip_ap_3d(t:TensorMask3D'>))

flips an image anterior posterior

{% endraw %} {% raw %}

TensorMask3D.flip_ap_3d[source]

TensorMask3D.flip_ap_3d(t:TensorMask3D'>))

flips an image anterior posterior

{% endraw %} {% raw %}

TensorDicom3D.flip_cc_3d[source]

TensorDicom3D.flip_cc_3d(t:TensorMask3D'>))

flips an image cranio caudal

{% endraw %} {% raw %}

TensorMask3D.flip_cc_3d[source]

TensorMask3D.flip_cc_3d(t:TensorMask3D'>))

flips an image cranio caudal

{% endraw %} {% raw %}
{% endraw %} {% raw %}

class RandomFlip3D[source]

RandomFlip3D(p=0.75) :: RandTransform

Randomly flip alongside any axis with probability p

{% endraw %} {% raw %}
{% endraw %} {% raw %}
torch.stack((original, RandomFlip3D()(original, split_idx = 0), 
                       RandomFlip3D()(original, split_idx = 0), 
                       RandomFlip3D()(original, split_idx = 0))).show()
{% endraw %}

Rotating

Medical images should show no rotation, however with removal of the image file header, the pixel array might appear rotated when displayed and thus be introduced to the model rotated. Fruthermore, in some images the patients might be rotated to some degree. Thus rotation of 90 and 180° as well as substeps should be implemented.

{% raw %}

TensorDicom3D.rotate_90_3d[source]

TensorDicom3D.rotate_90_3d(t:TensorMask3D'>))

{% endraw %} {% raw %}

TensorMask3D.rotate_90_3d[source]

TensorMask3D.rotate_90_3d(t:TensorMask3D'>))

{% endraw %} {% raw %}

TensorDicom3D.rotate_270_3d[source]

TensorDicom3D.rotate_270_3d(t:TensorMask3D'>))

{% endraw %} {% raw %}

TensorMask3D.rotate_270_3d[source]

TensorMask3D.rotate_270_3d(t:TensorMask3D'>))

{% endraw %} {% raw %}

TensorDicom3D.rotate_180_3d[source]

TensorDicom3D.rotate_180_3d(t:TensorMask3D'>))

{% endraw %} {% raw %}

TensorMask3D.rotate_180_3d[source]

TensorMask3D.rotate_180_3d(t:TensorMask3D'>))

{% endraw %} {% raw %}

class RandomRotate3D[source]

RandomRotate3D(p=0.5) :: RandTransform

Randomly flip rotates the axial slices of the 3D image 90/180 or 270 degrees with probability p

{% endraw %} {% raw %}
{% endraw %} {% raw %}
torch.stack((original, RandomRotate3D()(original, split_idx = 0),  
             RandomRotate3D()(original, split_idx = 0),  RandomRotate3D()(original, split_idx = 0))).show()
{% endraw %}

Pytorch does not support rotation of 3D images, so some transformations need to be applied slicewise.

{% raw %}

TensorDicom3D.apply_along_dim[source]

TensorDicom3D.apply_along_dim(t:TensorMask3D'>), func, dim)

{% endraw %} {% raw %}

TensorMask3D.apply_along_dim[source]

TensorMask3D.apply_along_dim(t:TensorMask3D'>), func, dim)

{% endraw %} {% raw %}
{% endraw %} {% raw %}

class RandomRotate3DBy[source]

RandomRotate3DBy(p=0.5, degrees=(2, 2, 30), axis=[-1, -2, -3]) :: RandTransform

Randomly flip rotates the axial slices of the 3D image 90/180 or 270 degrees with probability p

{% endraw %} {% raw %}
{% endraw %} {% raw %}
tmp1 = RandomRotate3DBy()(original, split_idx = 0)
tmp2 = RandomRotate3DBy(p=1., degrees=(10, 10, 45),  axis=[-1, -2, -3])(original, split_idx = 0)
original.show()
tmp1.show()
tmp2.show()
{% endraw %}

Rotating by 90 (or 180 and 270) degrees should not be done via RandomRotate3DBy but by rotate_90_3d, as this is approximatly 28 times faster.

"Dihedral" transformation

As the 3D array can be flipped by three sides, but should only be rotated along the z axis, this is not a complete dihedral group. Still multiple combinations of flipping and rotating should be implemented:

  1. original (= flipp ll, roate 180 = same as original image)
  2. rotate 90
  3. rotate 180
  4. rotate 270
  5. flip ll (=flip ap, rotate 180)
  6. flip ap
  7. flip cc
  8. flip cc, rotate 90
  9. flip cc, rotate 180
  10. flip cc, rotate 270
  11. flip ll, rotate 90
  12. flipp ll, rotate 270
  13. flip ap, rotate 90
  14. flip ap rotate 270
  15. flip cc, flip ll, rotate 90
  16. flip cc, flipp ll, rotate 270
  17. flip cc, flip ap, rotate 90
  18. flip cc, flip ap rotate 270

I am not sure if this is complete...

{% raw %}

TensorDicom3D.dihedral3d[source]

TensorDicom3D.dihedral3d(x:TensorMask3D'>), k)

apply dihedral transforamtions to the 3D Dicom Tensor

{% endraw %} {% raw %}

TensorMask3D.dihedral3d[source]

TensorMask3D.dihedral3d(x:TensorMask3D'>), k)

apply dihedral transforamtions to the 3D Dicom Tensor

{% endraw %} {% raw %}

class RandomDihedral3D[source]

RandomDihedral3D(p=1.0, nm=None, before_call=None, **kwargs) :: RandTransform

randomly flip and rotate the 3D Dicom volume with a probability of p

{% endraw %} {% raw %}
{% endraw %} {% raw %}
dihedral = RandomDihedral3D()
torch.stack((original, dihedral(original, split_idx = 0), dihedral(original, split_idx = 0), 
                 dihedral(original, split_idx = 0),dihedral(original, split_idx = 0), 
                 dihedral(original, split_idx = 0))).show()
{% endraw %}

Random Crop

A reasonable approach for 3D medical images would be a presizing to uniform but to large volume and subsequent random cropping to the target dimension. As most areas of interest are located centrally in the image/volume some cropping can always be applied.
Also random cropping should be applied after any rotation, that is not in 90/180/270 degrees, so that empty margins are cropped.

{% raw %}

TensorDicom3D.crop_3d[source]

TensorDicom3D.crop_3d(t:TensorMask3D'>), crop_by:(<class 'int'>, <class 'float'>), perc_crop=False)

Similar to function crop_3d_tensor, but no checking for margin formats is done, as they were correctly passed to this function by RandomCrop3D.encodes

{% endraw %} {% raw %}

TensorMask3D.crop_3d[source]

TensorMask3D.crop_3d(t:TensorMask3D'>), crop_by:(<class 'int'>, <class 'float'>), perc_crop=False)

Similar to function crop_3d_tensor, but no checking for margin formats is done, as they were correctly passed to this function by RandomCrop3D.encodes

{% endraw %} {% raw %}

class RandomCrop3D[source]

RandomCrop3D(crop_by, rand_crop_xyz, perc_crop=False, p=1, **kwargs) :: RandTransform

Randomly crop the 3D volume with a probability of p The x axis is the "slice" axis, where no cropping should be done by default

Args crop_by: number of pixels or pecantage of pixel to be removed at each side. E.g. if (0, 5, 5), 0 pixel in the x axis, but 10 pixels in eacht y and z axis will be cropped (5 each side) rand_crop_xyz: range in which the cropping window is allowed to vary. perc_crop: if true, no absolute but relative number of pixels are cropped

{% endraw %} {% raw %}
{% endraw %} {% raw %}
Crop = RandomCrop3D((10,150,150), (10,100,100), False)

torch.stack((Crop(original, split_idx = 0), Crop(original, split_idx = 0), 
             Crop(original, split_idx = 0), Crop(original, split_idx = 0))).show(nrow = 11)
im = Crop(original).resize_3d((10, 100, 100))
{% endraw %} {% raw %}

class ResizeCrop3D[source]

ResizeCrop3D(crop_by, resize_to, perc_crop=False, p=1, **kwargs) :: RandTransform

A transform that before_call its state at each __call__

{% endraw %} {% raw %}
{% endraw %}

Mask Ereasing

Sometimes, the images are in an uniform format and the area of interes can be expected in a certain image region. A mask, which covers the areas not of interst can help to futher reduce the image volume

{% raw %}
crop_mask = TensorMask3D(torch.ones(4, 100, 20)).pad_to((10, 100, 100))
crop_mask = crop_mask + crop_mask.rotate_90_3d()
crop_mask = torch.where(crop_mask == 0, 0, 1)

crop_mask2 = TensorMask3D(torch.ones(10, 100, 20)).pad_to((10, 100, 100))
crop_mask2 = crop_mask2 + crop_mask2.rotate_90_3d()
crop_mask2 = torch.where(crop_mask2 == 0, 1, 0)

crop_mask.show()
crop_mask2.show()
{% endraw %} {% raw %}

class MaskErease[source]

MaskErease(mask, pad=None, p=1.0) :: DisplayedTransform

ereases image areas in dependence of a mask. Strips black spaces afterwards

{% endraw %} {% raw %}
{% endraw %} {% raw %}
MaskErease(mask = crop_mask)(im).show()
MaskErease(mask = crop_mask2)(im).show()
{% endraw %}

Random Change of Perspective

{% raw %}
im2 = TensorDicom3D.create('../samples/example_grid.nii.gz')
im2 = im2.unsqueeze(0)
im2.show()
{% endraw %} {% raw %}

class RandomPerspective3D[source]

RandomPerspective3D(input_size, p=0.5, distortion_scale=0.25) :: RandTransform

A transform that before_call its state at each __call__

{% endraw %} {% raw %}
{% endraw %} {% raw %}
RandomPerspective3D(im.size(-1), p = 1.)(im2, split_idx=0).show()
{% endraw %}

Further Perspective Distortions

Augmentations based on Grid resampling. Not as many possibilities as RandomPerspective, but does allow to fill empty areas with mirrored image and not just 0 padding.

Perspective warping

Light wrapping can mimic artifacts in MRI images or breathing artifacts in CT images

{% raw %}

warp_3d[source]

warp_3d(h, w, magnitude_h, magnitude_w)

{% endraw %} {% raw %}

TensorDicom3D.grid_tfms[source]

TensorDicom3D.grid_tfms(t:TensorMask3D'>), func, mode)

wrapper for grid tfms

{% endraw %} {% raw %}

TensorMask3D.grid_tfms[source]

TensorMask3D.grid_tfms(t:TensorMask3D'>), func, mode)

wrapper for grid tfms

{% endraw %} {% raw %}

class RandomWarp3D[source]

RandomWarp3D(p=0.5, max_magnitude=0.25) :: RandTransform

A transform that before_call its state at each __call__

{% endraw %} {% raw %}
{% endraw %} {% raw %}
RandomWarp3D(p=1, max_magnitude=0.5)(im2, split_idx = 0).show()
RandomWarp3D(p=1, max_magnitude=0.5)(im2, split_idx = 0).show()
RandomWarp3D(p=1, max_magnitude=0.5)(im2, split_idx = 0).show()
{% endraw %}

Sheering

{% raw %}

sheer_3d[source]

sheer_3d(h, w, magnitude_h, magnitude_w)

applies a random sheer to the tenspr

{% endraw %} {% raw %}

class RandomSheer3D[source]

RandomSheer3D(p=0.5, max_magnitude=0.25) :: RandomWarp3D

A transform that before_call its state at each __call__

{% endraw %} {% raw %}
{% endraw %} {% raw %}
RandomSheer3D(p=1, max_magnitude=0.5)(im2, split_idx = 0).show()
RandomSheer3D(p=1, max_magnitude=0.5)(im2, split_idx = 0).show()
RandomSheer3D(p=1, max_magnitude=0.5)(im2, split_idx = 0).show()
{% endraw %}

Trapezoid

{% raw %}

trapezoid_3d[source]

trapezoid_3d(h, w, magnitude)

applies a random sheer to the tenspr

{% endraw %} {% raw %}

class RandomTrapezoid3D[source]

RandomTrapezoid3D(p=0.5, max_magnitude=0.25) :: RandomWarp3D

A transform that before_call its state at each __call__

{% endraw %} {% raw %}
{% endraw %} {% raw %}
RandomTrapezoid3D(p=1, max_magnitude=0.5)(im2, split_idx = 0).show()
RandomTrapezoid3D(p=1, max_magnitude=0.5)(im2, split_idx = 0).show()
RandomTrapezoid3D(p=1, max_magnitude=0.5)(im2, split_idx = 0).show()
{% endraw %}

Random Gaussian noise

Older scanners with lower field strength (MRI), fewer slices (CT), older algorithms can be more noise. So adding some random noise to the data, could improve model performance.

{% raw %}

TensorDicom3D.gaussian_noise[source]

TensorDicom3D.gaussian_noise(t:TensorDicom3D, std)

{% endraw %} {% raw %}

class RandomNoise3D[source]

RandomNoise3D(p=0.5, std_range=[0.01, 0.1]) :: RandTransform

A transform that before_call its state at each __call__

{% endraw %} {% raw %}
{% endraw %} {% raw %}
from faimed3d.preprocess import mean_scale
Noise= RandomNoise3D(p=1)
RandomNoise3D(p=1)(im.mean_scale(), split_idx=0).show()
RandomNoise3D(p=1)(im.mean_scale(), split_idx=0).show()
RandomNoise3D(p=1)(im.mean_scale(), split_idx=0).show()
RandomNoise3D(p=1)(im.mean_scale(), split_idx=0).show()
{% endraw %}

Gaussian Blur

{% raw %}

class RandomBlur3D[source]

RandomBlur3D(p=0.5, kernel_size_range=[5, 11], sigma=0.5) :: RandTransform

A transform that before_call its state at each __call__

{% endraw %} {% raw %}
{% endraw %} {% raw %}
RandomBlur3D(p=1., sigma = 10)(im, split_idx=0).show()
{% endraw %}

Lightning transforms

Simple brightness and contrast controlls can be dona via a linear function:

x = alpha * x_i + beta  

here x_i is the respective pixel, alpha allows simple contrast control, beta allows simple brightness control.

{% raw %}

TensorDicom3D.rescale[source]

TensorDicom3D.rescale(t:TensorDicom3D, new_min=0, new_max=1)

{% endraw %} {% raw %}

TensorDicom3D.adjust_brightness[source]

TensorDicom3D.adjust_brightness(x:TensorDicom3D, beta)

{% endraw %} {% raw %}

class RandomBrightness3D[source]

RandomBrightness3D(p=0.5, beta_range=[-0.3, 0.3]) :: RandTransform

A transform that before_call its state at each __call__

{% endraw %} {% raw %}
{% endraw %} {% raw %}
torch.stack((im.mean_scale(), 
             RandomBrightness3D(p=1., beta_range=[0.9, 1])(im.mean_scale(), split_idx = 0), 
             RandomBrightness3D(p=1., beta_range=[-0.9, -1])(im.mean_scale(), split_idx = 0))).show()
{% endraw %}

Contrast

{% raw %}

TensorDicom3D.adjust_contrast[source]

TensorDicom3D.adjust_contrast(x:TensorDicom3D, alpha)

{% endraw %} {% raw %}

class RandomContrast3D[source]

RandomContrast3D(p=0.6, alpha_range=[0.7, 2.0]) :: RandTransform

A transform that before_call its state at each __call__

{% endraw %} {% raw %}
{% endraw %} {% raw %}
im.mean_scale().show()
RandomContrast3D(p=1.)(im.mean_scale(), split_idx = 0).show()
RandomContrast3D(p=1.)(im.mean_scale(), split_idx = 0).show()
{% endraw %}
{% raw %}
def elastic_transform_3d(image, labels=None, alpha=4, sigma=35, bg_val=0.1):
    """
    Elastic deformation of images as described in
    Simard, Steinkraus and Platt, "Best Practices for
    Convolutional Neural Networks applied to Visual
    Document Analysis", in
    Proc. of the International Conference on Document Analysis and
    Recognition, 2003. 

    Modified from:
    https://gist.github.com/chsasank/4d8f68caf01f041a6453e67fb30f8f5a
    https://github.com/fcalvet/image_tools/blob/master/image_augmentation.py#L62

    Modified to take 3D inputs
    Deforms both the image and corresponding label file
    image linear/trilinear interpolated

    Label volumes nearest neighbour interpolated
    """
    assert image.ndim == 3
    shape = image.shape
    dtype = image.dtype

    # Define coordinate system
    coords = np.arange(shape[0]), np.arange(shape[1]), np.arange(shape[2])

    # Initialize interpolators
    im_intrps = RegularGridInterpolator(coords, image,
                                                method="linear",
                                                bounds_error=False,
                                                fill_value=bg_val)

    # Get random elastic deformations
    dx = gaussian_filter((np.random.rand(*shape) * 2 - 1), sigma,
                         mode="constant", cval=0.) * alpha
    dy = gaussian_filter((np.random.rand(*shape) * 2 - 1), sigma,
                         mode="constant", cval=0.) * alpha
    dz = gaussian_filter((np.random.rand(*shape) * 2 - 1), sigma,
                        mode="constant", cval=0.) * alpha

    # Define sample points
    x, y, z = np.mgrid[0:shape[0], 0:shape[1], 0:shape[2]]
    indices = np.reshape(x + dx, (-1, 1)), \
             np.reshape(y + dy, (-1, 1)), \
             np.reshape(z + dz, (-1, 1))

    # Interpolate 3D image image
    image = np.empty(shape=image.shape, dtype=dtype)
    image = im_intrps(indices).reshape(shape)

    # Interpolate labels
    if labels is not None:
        lab_intrp = RegularGridInterpolator(coords, labels,
                                           method="nearest",
                                           bounds_error=False,
                                           fill_value=0)

        labels = lab_intrp(indices).reshape(shape).astype(labels.dtype)
        return image, labels

    return image
{% endraw %}

Putting it all together

A good workflow would be to apply random crop to all images after one transformation. For this, the images should be presized to a size, just some pixels larger then desired, then transformed and then cropped to the final size. Using this approach empty space, which e.g. appears after RandomRotate3DBy will be cropped and not influence the accuracy of the model. One only has to be careful, that the region of interest, e.g. the prostate, will be in every cropped image.

{% raw %}
Crop = RandomCrop3D((2,10,10), (1,2,2))

tfms = [RandomBrightness3D(), RandomContrast3D(), RandomWarp3D(), RandomDihedral3D(), RandomNoise3D(), RandomRotate3DBy()]
tfms = [Pipeline([RandomBrightness3D(p=1.), Crop], split_idx = 0), 
        Pipeline([RandomContrast3D(p=1.), Crop], split_idx = 0), 
        Pipeline([RandomWarp3D(p=1.), Crop], split_idx = 0), 
        Pipeline([RandomDihedral3D(p=1.), Crop], split_idx = 0), 
        Pipeline([RandomNoise3D(p=1.), Crop], split_idx = 0), 
        Pipeline([RandomRotate3DBy(p=1.), Crop], split_idx = 0)]
{% endraw %} {% raw %}
comp = setup_aug_tfms(tfms)
comp
[Pipeline: RandomBrightness3D -- {'p': 1.0} -> RandomCrop3D -- {'p': 1},
 Pipeline: RandomContrast3D -- {'p': 1.0} -> RandomCrop3D -- {'p': 1},
 Pipeline: RandomWarp3D -- {'p': 1.0} -> RandomCrop3D -- {'p': 1},
 Pipeline: RandomDihedral3D -- {'p': 1.0} -> RandomCrop3D -- {'p': 1},
 Pipeline: RandomNoise3D -- {'p': 1.0} -> RandomCrop3D -- {'p': 1},
 Pipeline: RandomRotate3DBy -- {'p': 1.0, 'degrees': (2, 2, 30), 'axis': [-1, -2, -3]} -> RandomCrop3D -- {'p': 1}]
{% endraw %} {% raw %}
ims = [t(im).squeeze() for t in tfms]
torch.stack(ims).show(nrow = 6)
{% endraw %}

Creating a pseudo color channel

Pytorch expects the images in the following format:

B C D H W

Here:

  • B = Batch dimension
  • C = Number of Channels (e.g. color)
  • D = Depth of the image (= number of slices)
  • H = Height of the image
  • W = Width of the image
{% raw %}
@patch
def make_pseudo_color(t: (TensorDicom3D, TensorMask3D)): 
    '''
    The 3D CNN still expects color images, so a pseudo color image needs to be created as long as I don't adapt the 3D CNN
    '''
    if t.ndim == 3:
        return t.unsqueeze(0).float()  
    elif t.ndim == 4:
        return t.unsqueeze(1).float()
    else: 
        return t  

class PseudoColor(RandTransform):
    split_idx, p = None, 1
    
    def __init__(self, p=1): 
        super().__init__(p=p)

    def __call__(self, b, split_idx=None, **kwargs):
        "change in __call__ to enforce, that the Transform is always applied on every dataset. "
        return super().__call__(b, split_idx=split_idx, **kwargs) 
    
    def encodes(self, x:(TensorDicom3D, TensorMask3D)): 
        return x.make_pseudo_color()
{% endraw %} {% raw %}
MakeColor = PseudoColor()
im.shape, MakeColor(im, split_idx = 0).shape
((10, 100, 100), (1, 10, 100, 100))
{% endraw %} {% raw %}
{% endraw %} {% raw %}

aug_transforms_3d[source]

aug_transforms_3d(p_all=0.1, warp=True, p_warp=None, sheer=True, p_sheer=None, trapezoid=True, p_trapezoid=None, dihedral=True, p_dihedral=None, brightness=True, p_brightness=None, contrast=True, p_contrast=None, noise=True, p_noise=None, rotate_by=True, p_rotate_by=None, flip=True, p_flip=None, rotate=True, p_rotate=None, blur=True, p_blur=None, crop=True, p_crop=1)

{% endraw %} {% raw %}
{% endraw %} {% raw %}
tmp = Pipeline(aug_transforms_3d(p_all = 1.), split_idx=0)(im)
print(tmp.size())
tmp.show()
(10, 100, 100)
{% endraw %}

Mask Transformations

Transformations for the mask in segmentation tasks. If it is a multilabel segmentation task, the mask needs to be converted into a one hot encoded tensor.

{% raw %}

TensorMask3D.clamp_to_range[source]

TensorMask3D.clamp_to_range(x:TensorMask3D, lwr, upr)

{% endraw %} {% raw %}

class ClampMask3D[source]

ClampMask3D(lwr=0, upr=1, p=1) :: RandTransform

Clamps/Clips mask value to a range

{% endraw %} {% raw %}
{% endraw %} {% raw %}

TensorMask3D.reduce_classes[source]

TensorMask3D.reduce_classes(x:TensorMask3D, classes:(<class 'list'>, <class 'tuple'>))

{% endraw %} {% raw %}

class ReduceClasses[source]

ReduceClasses(reduce_to, p=1) :: RandTransform

Removes classes from mask

{% endraw %} {% raw %}
{% endraw %} {% raw %}
mask.reduce_classes([2]).unique()
tensor([0., 1.])
{% endraw %}